package com.llbnk;

/**
 * 令牌桶限流法
 * @author shkstart
 * @create 2022-10-16 20:19
 */
public class TokenBucketRateLimiter implements RateLimiter {
    // 令牌桶的容量
    private final long capacity;
    // 令牌发放速率
    private final long generatedPerSecond;
    // 最后一个令牌发放的时间
    private long lastTokenTime = System.currentTimeMillis();
    // 当前令牌数量
    private long currentTokens;

    public TokenBucketRateLimiter(long capacity, long generatedPerSecond) {
        this.capacity = capacity;
        this.generatedPerSecond = generatedPerSecond;
    }

    @Override
    public synchronized boolean tryAcquire() {
        /*
          计算当前令牌的数量
          请求时间在最后令牌产生的时间相差大于等于1s
          1.重新计算令牌桶中的令牌数量
          2.将最后一个令牌发放时间重置为当前时间
         */
        long now = System.currentTimeMillis();
        //当前时间距离上次一访问时间之间产生了多少个令牌(now - lastTokenTime) / 1000 * generatedPerSecond
        //(now - lastTokenTime) / 1000这个是计算时间距离上次一访问时间间隔多少秒 generatedPerSecond代表的是每秒钟产生了多少令牌
        //newPermits代表的是在这段时间内产生的令牌数量
        //currentTokens当前的令牌数量 因为你的令牌数量肯定是要小于令牌桶的容量的
        //所以取currentTokens + newPermits, capacity小的值
        //然后更新时间把当前时间变成下一次时间
        //此时有一个请求来访问所以就相当于拿走一个令牌所以currentTokens-1即可
        //如果当前currentTokens没有数值就没有令牌直接禁止访问就完事了
        if (now - lastTokenTime >= 1000) {
            long newPermits = (now - lastTokenTime) / 1000 * generatedPerSecond;
            currentTokens = Math.min(currentTokens + newPermits, capacity);
            lastTokenTime = now;
        }
        if (currentTokens > 0) {
            currentTokens--;
            return true;
        }
        return false;
    }
}